package com.hx.miniblog.util.utils;

import com.alibaba.fastjson.JSON;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.hx.miniblog.exception.MiniBlogException;
import com.hx.miniblog.pojo.vo.LoginAccountVo;
import com.hx.miniblog.util.constant.Constant;
import com.hx.miniblog.util.constant.RedisKeyConstant;
import io.vavr.control.Try;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Calendar;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author wenhao hu
 * @title: JwtUtil
 * @projectName miniBlog
 * @description: TODO
 * @date 2021/12/2810:50
 */
@Component
@Slf4j
public class JwtUtil {

    @Resource
    RedisUtil redisUtil;

    private static final Integer EXPIRE = 30;

    private RSAPrivateKey privateKey;
    private RSAPublicKey publicKey;

    @PostConstruct
    public void load(){
        Try.run(() -> {
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
            keyPairGen.initialize(1024);//2048
            KeyPair keyPair = keyPairGen.generateKeyPair();
            publicKey = (RSAPublicKey) keyPair.getPublic();
            privateKey = (RSAPrivateKey) keyPair.getPrivate();
        }).onFailure(e -> log.error(this.getClass().getSimpleName(), e));
    }

    /**
     * 签发对象：这个用户的id
     * 签发时间：现在
     * 有效时间：630分钟
     * 载荷内容：暂时设计为：这个人的名字，这个人的昵称
     * 加密密钥：这个人的id加上一串字符串
     */
    public String createToken(LoginAccountVo loginVo) {
        Calendar nowTime = Calendar.getInstance();
        nowTime.add(Calendar.MINUTE, EXPIRE);
        Date expiresDate = nowTime.getTime();
        //将Token放入redis
        String token = JWT.create().withAudience(loginVo.getId())
                .withIssuedAt(new Date())
                .withExpiresAt(expiresDate)
                .withClaim("data", JSON.toJSONString(loginVo))
                .sign(Algorithm.RSA256(publicKey,privateKey));
        redisUtil.set(RedisKeyConstant.LOGIN_TOKEN+loginVo.getId(),token);
        return token;
    }

    /**
     * 检验合法性，其中secret参数就应该传入的是用户的id
     *
     * @param token
     * @throws MiniBlogException
     */
    public Boolean verifyToken(String token) throws MiniBlogException {
        try {
            JWTVerifier verifier = JWT.require(Algorithm.RSA256(publicKey,privateKey)).build();
            if (Objects.nonNull(verifier.verify(token))){
                //判断是否有token，有的话可以刷新一下时间，防止掉线
                LoginAccountVo loginVo = getLoginVo(token);
                //检查这个用户是否登录，再检查这个token是否是刚才的token
                if(redisUtil.hasKey(RedisKeyConstant.LOGIN_TOKEN+loginVo.getId())
                        && redisUtil.get(RedisKeyConstant.LOGIN_TOKEN+loginVo.getId()).equals(token)){
                    redisUtil.expire(RedisKeyConstant.LOGIN_TOKEN+token,EXPIRE, TimeUnit.MINUTES);
                    return true;
                }
            }
            throw new MiniBlogException(Constant.MESSAGE.TOKEN_EXPIRE);
        }catch (Exception e){
            throw new MiniBlogException(Constant.MESSAGE.TOKEN_EXPIRE);
        }
    }

    public static LoginAccountVo getLoginVo(String token) {
        String s = JWT.decode(token).getClaim(Constant.JWT_CONSTANT.PAYLOAD_DATA).asString();
        return JSON.parseObject(s, LoginAccountVo.class);
    }
}